﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;

namespace UT3Bots.Communications
{
	[AttributeUsage(AttributeTargets.Field)]
	public class StringValueAttribute : Attribute
	{

		#region Properties
		/// <summary>
		/// Holds the stringvalue for a value in an enum.
		/// </summary>
		public string StringValue { get; protected set; }
		#endregion

		/// <summary>
		/// Constructor used to init a StringValue Attribute
		/// </summary>
		/// <param name="value"></param>
		public StringValueAttribute(string value)
		{
			this.StringValue = value;
		}
	}




	internal enum EventMessage
	{
		[StringValue("")]
		None = 0,

		/// <summary>
		/// The message indicating that this a status update
		/// </summary>
		[StringValue("STATE")]
		STATE,

		/// <summary>
		/// The message containing info about the game that you have connected to
		/// </summary>
		[StringValue("INFO")]
		INFO,

		/// <summary>
		/// The message that the match is over
		/// </summary>
		[StringValue("ENDMATCH")]
		MATCH_ENDED,

		/// <summary>
		/// The message telling you that the match has not yet started
		/// </summary>
		[StringValue("WAITFORSPAWN")]
		WAITING_FOR_SPAWN,

		/// <summary>
		/// The message telling you that you've respawned and ready to fight
		/// </summary>
		[StringValue("SPAWNED")]
		SPAWNED,

		/// <summary>
		/// A message that a player has died
		/// </summary>
		[StringValue("KILLED")]
		KILLED,

		/// <summary>
		/// The message that this bot has died
		/// </summary>
		[StringValue("DIED")]
		DIED,

		/// <summary>
		/// The message that this bot might fall
		/// </summary>
		[StringValue("FOUNDFALL")]
		FOUNDFALL,

		/// <summary>
		/// The message about having seen another player
		/// </summary>
		[StringValue("SEENPLAYER")]
		SEEN_PLAYER,

		/// <summary>
		/// The message about having bumped into something
		/// </summary>
		[StringValue("BUMPED")]
		BUMPED,

		/// <summary>
		/// The message about having bumped into a wall
		/// </summary>
		[StringValue("HITWALL")]
		HIT_WALL,

		/// <summary>
		/// The message about having heard a noise nearby
		/// </summary>
		[StringValue("HEARDNOISE")]
		HEARD_NOISE,

		/// <summary>
		/// The message about being damages
		/// </summary>
		[StringValue("DAMAGED")]
		DAMAGED,

		/// <summary>
		/// The message about hearing a chat message
		/// </summary>
		[StringValue("CHAT")]
		CHAT,

		/// <summary>
		/// The message about someone taunting you
		/// </summary>
		[StringValue("TAUNTED")]
		TAUNTED,

		/// <summary>
		/// The message about the bot's weapon changing
		/// </summary>
		[StringValue("WEAPONCHANGED")]
		WEAPON_CHANGED,

		/// <summary>
		/// The message about the bot picking up an item
		/// </summary>
		[StringValue("GOTPICKUP")]
		GOT_PICKUP,

		/// <summary>
		/// The message about the path returned from a 'get path' call
		/// </summary>
		[StringValue("PATH")]
		PATH

	}

	internal enum CommandMessage
	{
		[StringValue("")]
		None = 0,

		/// <summary>
		/// The command to setup the bot
		/// </summary>
		[StringValue("INIT")]
		INITIALIZE,

		/// <summary>
		/// The command to Fire your gun
		/// </summary>
		[StringValue("FIRE")]
		FIRE,

		/// <summary>
		/// The command to stop firing your gun
		/// </summary>
		[StringValue("STOPFIRE")]
		STOP_FIRE,

		/// <summary>
		/// The command to stop the bot moving
		/// </summary>
		[StringValue("STOP")]
		STOP,

		/// <summary>
		/// The command to Run to a location
		/// </summary>
		[StringValue("RUNTO")]
		RUN_TO,

		/// <summary>
		/// The command to Strafe to a location while looking at a target
		/// </summary>
		[StringValue("STRAFETO")]
		STRAFE_TO,

		/// <summary>
		/// The command to rotate the bot to point at a specific location
		/// </summary>
		[StringValue("ROTATETO")]
		ROTATE_TO,

		/// <summary>
		/// The command to rotate the bot by a certain amount
		/// </summary>
		[StringValue("ROTATEBY")]
		ROTATE_BY,

		/// <summary>
		/// The command to make the bot jump
		/// </summary>
		[StringValue("JUMP")]
		JUMP,

		/// <summary>
		/// The command to make the bot send a chat message
		/// </summary>
		[StringValue("SENDCHAT")]
		SENDCHAT,

		/// <summary>
		/// The command to make the bot send a taunt message
		/// </summary>
		[StringValue("SENDTAUNT")]
		SENDTAUNT,

		/// <summary>
		/// The command to make the bot send an emote
		/// </summary>
		[StringValue("SENDEMOTE")]
		SENDEMOTE,

		/// <summary>
		/// The command to make the bot walk or run
		/// </summary>
		[StringValue("SETWALK")]
		SETWALK,

		/// <summary>
		/// The command to make the bot change weapon
		/// </summary>
		[StringValue("CHANGEWEAPON")]
		CHANGE_WEAPON,

		/// <summary>
		/// The command to request a list of path nodes to a location
		/// </summary>
		[StringValue("GETPATH")]
		GET_PATH
	}

	internal enum InfoMessage
	{
		[StringValue("")]
		None = 0,

		/// <summary>
		/// The message stating that this is the beginning of a state update batch
		/// </summary>
		[StringValue("BEGIN")]
		BEGIN,

		/// <summary>
		/// The message stating that this is the end of a state update batch
		/// </summary>
		[StringValue("END")]
		END,

		/// <summary>
		/// The message about player scores
		/// </summary>
		[StringValue("SCORE")]
		SCORE_INFO,

		/// <summary>
		/// The message about player info
		/// </summary>
		[StringValue("PLAYER")]
		PLAYER_INFO,

		/// <summary>
		/// The message about navigation points
		/// </summary>
		[StringValue("NAV")]
		NAV_INFO,

		/// <summary>
		/// The message about power ups that are on the map
		/// </summary>
		[StringValue("PICKUP")]
		PICKUP_INFO,

		/// <summary>
		/// The message about the bot itself
		/// </summary>
		[StringValue("SELF")]
		SELF_INFO,

		/// <summary>
		/// The message about the game
		/// </summary>
		[StringValue("GAME")]
		GAME_INFO
	}


	internal static class Extensions
	{
		private static Dictionary<string, EventMessage> EventMessages;
		private static Dictionary<string, InfoMessage> InfoMessages;
		private static Dictionary<string, WeaponType> WeaponTypes;
		private static Dictionary<string, AmmoType> AmmoTypes;
		private static Dictionary<string, HealthType> HealthTypes;
		private static Dictionary<string, ArmorType> ArmorTypes;

		static Extensions()
		{
			EventMessages = new Dictionary<string, EventMessage>();
			string[] names = GetNames(typeof(EventMessage));
			foreach (string name in names)
			{
				EventMessage type = ((EventMessage)Enum.Parse(typeof(EventMessage), name, true));
				EventMessages.Add(type.GetStringValue(), type);
			}

			InfoMessages = new Dictionary<string, InfoMessage>();
			names = GetNames(typeof(InfoMessage));
			foreach (string name in names)
			{
				InfoMessage type = ((InfoMessage)Enum.Parse(typeof(InfoMessage), name, true));
				InfoMessages.Add(type.GetStringValue(), type);
			}

			WeaponTypes = new Dictionary<string, WeaponType>();
			names = GetNames(typeof(WeaponType));
			foreach (string name in names)
			{
				WeaponType type = ((WeaponType)Enum.Parse(typeof(WeaponType), name, true));
				WeaponTypes.Add(type.GetStringValue(), type);
			}

			AmmoTypes = new Dictionary<string, AmmoType>();
			names = GetNames(typeof(AmmoType));
			foreach (string name in names)
			{
				AmmoType type = ((AmmoType)Enum.Parse(typeof(AmmoType), name, true));
				AmmoTypes.Add(type.GetStringValue(), type);
			}

			HealthTypes = new Dictionary<string, HealthType>();
			names = GetNames(typeof(HealthType));
			foreach (string name in names)
			{
				HealthType type = ((HealthType)Enum.Parse(typeof(HealthType), name, true));
				HealthTypes.Add(type.GetStringValue(), type);
			}

			ArmorTypes = new Dictionary<string, ArmorType>();
			names = GetNames(typeof(ArmorType));
			foreach (string name in names)
			{
				ArmorType type = ((ArmorType)Enum.Parse(typeof(ArmorType), name, true));
				ArmorTypes.Add(type.GetStringValue(), type);
			}

		}

		static string[] GetNames(Type t)
		{
			List<string> names = new List<string>();
			FieldInfo[] infoCollection = t.GetFields(BindingFlags.Public | BindingFlags.Static);
			foreach (FieldInfo fi in infoCollection)
			{
				names.Add(fi.Name);
			}
			return names.ToArray();
		}

		internal static string GetStringValue(this Enum value)
		{
			// Get the type
			Type type = value.GetType();

			// Get fieldinfo for this type
			FieldInfo fieldInfo = type.GetField(value.ToString());

			// Get the stringvalue attributes
			StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
				typeof(StringValueAttribute), false) as StringValueAttribute[];

			// Return the first if there was a match.
			return attribs.Length > 0 ? attribs[0].StringValue : null;
		}


		internal static EventMessage GetAsEvent(this string value)
		{
			if (EventMessages.ContainsKey(value))
			{
				return EventMessages[value];
			}

			return EventMessage.None;
		}

		internal static InfoMessage GetAsInfo(this string value)
		{
			if (InfoMessages.ContainsKey(value))
			{
				return InfoMessages[value];
			}

			return InfoMessage.None;
		}

		internal static WeaponType GetAsWeaponType(this string value)
		{
			if (WeaponTypes.ContainsKey(value))
			{
				return WeaponTypes[value];
			}

			return WeaponType.None;
		}

		internal static AmmoType GetAsAmmoType(this string value)
		{
			if (AmmoTypes.ContainsKey(value))
			{
				return AmmoTypes[value];
			}

			return AmmoType.None;
		}

		internal static HealthType GetAsHealthType(this string value)
		{
			if (HealthTypes.ContainsKey(value))
			{
				return HealthTypes[value];
			}

			return HealthType.None;
		}

		internal static ArmorType GetAsArmorType(this string value)
		{
			if (ArmorTypes.ContainsKey(value))
			{
				return ArmorTypes[value];
			}

			return ArmorType.None;
		}
	}

}
